/*
 * 上海远境文化传媒有限公司 版权所有
 */
package models.base;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import ext.dbutils.DBUtils;
import ext.jpa.UUIDModelWithDiff;
import helper.GlobalConfig;
import jodd.bean.BeanCopy;
import models.vo.DiffVO;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.Diff;
import org.apache.commons.lang3.builder.DiffResult;
import org.hibernate.annotations.Index;
import play.Logger;
import play.db.jpa.Model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Lob;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * 实体差异历史信息.
 *
 * 注: DiffHistory使用 Long 主键, 避免了显示排序用时间的问题, 也可以节约空间.
 *
 * 迁移时, 导出的sql不要id, 直接重新insert即可, id不会被其它表用做外键.
 */
@Entity
@Table(name = "diff_histories", schema = GlobalConfig.MIGAO_DB_SCHEMA)
@org.hibernate.annotations.Table(
        appliesTo = "diff_histories",
        indexes = {
                @Index(name = "idx_diff_histories_ro", columnNames = {"ref_object_type", "ref_object_id"}),
                @Index(name = "idx_diff_histories_cb", columnNames = "created_by")
        }
)
public class DiffHistory extends Model {

    /**
     * 对应对象类型, 使用Type.getClass.simpleName
     */
    @Column(name = "ref_object_type", columnDefinition = "varchar(30)")
    public String refObjectType;

    /**
     * 对应对象ID.
     */
    @Column(name = "ref_object_id", columnDefinition = "char(22)")
    public String refObjectId;

    /**
     * 变更说明.
     */
    @Column(name = "remark", length = 200)
    public String remark;

    /**
     * 修改人.  User.id
     */
    @Column(name = "created_by", columnDefinition = "char(22)")
    public String changedBy;

    /**
     * 当前对象的json快照.
     */
    @Lob
    @Column(name = "clob_current_json")
    public String currentJson;

    /**
     * 当前变更的json结果.
     *
     * TODO: 要把diff_histories旧字段changed_json的值复制到clob_changed_json
     */
    @Lob
    @Column(name = "clob_changed_json")
    public String changedJson;

    /**
     * 创建时间.
     */
    @Column(name = "created_at")
    public Date createdAt;


    @Transient
    public String getModifierShowName() {
        if (StringUtils.isNotBlank(this.changedBy)) {
            AdminUser user = AdminUser.findById(this.changedBy);
            if (user != null) {
                return user.name + " (用户)";
            }
        }
        return this.changedBy; //找不到，原样返回，如：支付通道， 系统自动处理
    }

    private static ThreadLocal<UUIDModelWithDiff> _prevObject = new ThreadLocal<>();

    /**
     * 保存前一对象
     *
     * @param prevObject     要保存的对象
     * @param toClonedObject 使用 new T()
     */
    public static void storePrevObject(UUIDModelWithDiff prevObject, UUIDModelWithDiff toClonedObject) {
        _prevObject.remove();
        new BeanCopy(prevObject, toClonedObject).declared(true).copy();
        toClonedObject.id = null;
        _prevObject.set(toClonedObject);
    }

    public static void storePrevObjectForNew() {
        _prevObject.remove();
    }

    /**
     * 保存对象变更.
     *
     * @param changedByUserId 变更者 ID, User
     * @param currentObject 当前对象
     * @param remark 变更说明
     * @return 变更历史
     */
    public static DiffHistory recordHistory(String changedByUserId, UUIDModelWithDiff currentObject, String remark) {
        if (DBUtils.isOracle()) {
            Logger.info("LOG04685: Oracle暂时不支持Lob");
            return null;
        }

        UUIDModelWithDiff prevbject = _prevObject.get();
        Gson gson = new Gson();
        DiffHistory diffHistory = new DiffHistory();
        diffHistory.refObjectType = currentObject.getClass().getSimpleName().toLowerCase();
        diffHistory.refObjectId = currentObject.id;
        diffHistory.remark = remark;
        diffHistory.changedBy = changedByUserId;
        diffHistory.createdAt = new Date();

        if (prevbject == null) {
            diffHistory.save();
            return diffHistory;
        }

        DiffResult diffResult = prevbject.diff(currentObject);
        if (diffResult.getNumberOfDiffs() > 0) {  // 有变更才保存
            List<DiffVO> diffVOs = new ArrayList<>();
            for (Diff<?> diff : diffResult.getDiffs()) {
                DiffVO diffVO = new DiffVO();
                diffVO.p = diff.getFieldName();
                diffVO.from = String.valueOf(diff.getLeft());
                diffVO.to = String.valueOf(diff.getRight());
                Logger.debug("LOG03610: diffVO=%s", diffVO);
                diffVOs.add(diffVO);
            }
            diffHistory.changedJson = gson.toJson(diffVOs);
            diffHistory.save();
        }
        return diffHistory;
    }

    @Transient
    public List<DiffVO> getDiffList() {
        if (this.changedJson != null) {
            Type listType = new TypeToken<List<DiffVO>>(){}.getType();
            return new Gson().fromJson(this.changedJson, listType);
        }
        return Collections.emptyList();
    }

    /**
     * 用于显示修改历史，按时间倒序.
     */
    public static List<DiffHistory> findByCorpAndRefObjectIdOrderDesc(String refObjectType, String refObjectId) {
        return DiffHistory.find("refObjectType=?1 and refObjectId=?2 order by id desc", refObjectType, refObjectId).fetch(50); //保护：最多看50个历史
    }
}
